﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using DcsGauges.Shared.Packets;
using NLog;

namespace DcsGauges.Shared.Network
{
    public class NetworkChannel : INetworkChannel
    {
        #region Fields & Properties
        
        private static Logger   logger                  = LogManager.GetCurrentClassLogger();

        private Socket          socket                  = null;
        private StringBuffer    contentBuffer           = new StringBuffer();
        private byte[]          buffer                  = new byte[SharedConstants.NETWORK_BUFFER_SIZE];
        private PacketBuilder   packetBuilder           = new PacketBuilder();
        public int              Ending                  { get; set; } //????
        #endregion


        #region Constructors
        
        public NetworkChannel(Socket socket)
        {
            this.socket             = socket;
            this.socket.BeginReceive(this.buffer, 0, SharedConstants.NETWORK_BUFFER_SIZE, SocketFlags.None, EndReceive, null);
        }

        #endregion


        #region Operations

        public void Send(Packets.Packet packet)
        {
            byte[] content = this.packetBuilder.Build(packet);
            this.socket.Send(content);
        }

        #endregion


        #region Asnyc Callbacks

        private void EndReceive(IAsyncResult result)
        {
            int received = this.socket.EndReceive(result);

            string receivedString = Encoding.UTF8.GetString(this.buffer, 0, received);
            logger.Log(LogLevel.Debug, "Received: {0}", receivedString);

            this.contentBuffer.Append(this.buffer, received);
            Packet packet       = this.packetBuilder.Build(contentBuffer);
            if(packet != null)
            {
                switch(packet.PacketType)
                {
                    case Packet.PacketTypes.Data:       this.FireDataPacketReceived(packet as DataPacket); break;
                    case Packet.PacketTypes.Handshake:  this.FireHandshakePacketReceived(packet as HandshakePacket); break;
                    case Packet.PacketTypes.Commands:   this.FireCommandsPacketReceivedEvent(packet as CommandsPacket); break;
                    case Packet.PacketTypes.Unknown:    logger.Error(string.Format("Received unknown network packet with content: {0}", packet.ToString())); break;
                }
            }

            this.socket.BeginReceive(this.buffer, 0, SharedConstants.NETWORK_BUFFER_SIZE, SocketFlags.None, EndReceive, null);
        }

        #endregion


        #region Helpers

        private void FireDataPacketReceived(DataPacket packet)
        {
            if (this.DataPacketReceivedEvent != null)
            {
                this.DataPacketReceivedEvent(this, new PacketReceivedEventArgs<DataPacket>(packet));
            }
        }

        private void FireHandshakePacketReceived(HandshakePacket packet)
        {
            if (this.HandshakePacketReceivedEvent != null)
            {
                this.HandshakePacketReceivedEvent(this, new PacketReceivedEventArgs<HandshakePacket>(packet));
            }
        }

        private void FireCommandsPacketReceivedEvent(CommandsPacket packet)
        {
            if (this.CommandsPacktReceivedEvent != null)
            {
                this.CommandsPacktReceivedEvent(this, new PacketReceivedEventArgs<CommandsPacket>(packet));
            }
        }

        #endregion


        #region Events

        public event EventHandler<PacketReceivedEventArgs<DataPacket>>              DataPacketReceivedEvent;
        public event EventHandler<PacketReceivedEventArgs<HandshakePacket>>         HandshakePacketReceivedEvent;
        public event EventHandler<PacketReceivedEventArgs<CommandsPacket>>          CommandsPacktReceivedEvent;

        #endregion
    }
}
